
import {
  Blob,
  btoa,
  createImageBitmap,
  CSSStyleDeclaration,
  performance,
  document,
  DOMParser,
  EventTarget,
  fetch,
  Headers,
  HTMLCanvasElement,
Image,
  HTMLImageElement,
  ImageBitmap,
  location,
  navigator,
  Request,
  requestAnimationFrame,
  cancelAnimationFrame,
  Response,
  URL,
  window,
  self,
  WebAssembly,
  Worker,
  XMLHttpRequest,
ImageData,
TextDecoder,
  core
  } from 'dhtml-weixin';
/**
* Ascii generation is based on https://github.com/hassadee/jsascii/blob/master/jsascii.js
*
* 16 April 2012 - @blurspline
*/

class AsciiEffect {

  constructor(renderer, charSet = ' .:-=+*#%@', options = {}) {

      // ' .,:;=|iI+hHOE#`$';
      // darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/
      // ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split('');

      // Some ASCII settings

      const fResolution = options['resolution'] || 0.15; // Higher for more details
      const iScale = options['scale'] || 1;
      const bColor = options['color'] || false; // nice but slows down rendering!
      const bAlpha = options['alpha'] || false; // Transparency
      const bBlock = options['block'] || false; // blocked characters. like good O dos
      const bInvert = options['invert'] || false; // black is white, white is black
      const strResolution = options['strResolution'] || 'low';

      let width, height;

      const domElement = document.createElement('div');
      domElement.style.cursor = 'default';

      const oAscii = document.createElement('table');
      oAscii.id = "oAscii"
      domElement.appendChild(oAscii);

      let iWidth, iHeight;
      let oImg;

      this.setSize = function (w, h) {

          width = w;
          height = h;

          renderer.setSize(w, h);

          initAsciiSize();

      };


      this.render =  function (scene, camera) {

          renderer.render(scene, camera);
           asciifyImage(oAscii).then();

      };

      this.domElement = domElement;


      // Throw in ascii library from https://github.com/hassadee/jsascii/blob/master/jsascii.js (MIT License)

      function initAsciiSize() {

          iWidth = Math.round(width * fResolution);
          iHeight = Math.round(height * fResolution);

          oCanvas.width = iWidth;
          oCanvas.height = iHeight;
          // oCanvas.style.display = "none";
          // oCanvas.style.width = iWidth;
          // oCanvas.style.height = iHeight;

          oImg = renderer.domElement;

          /*if ( oImg.style.backgroundColor ) {

            oAscii.rows[ 0 ].cells[ 0 ].style.backgroundColor = oImg.style.backgroundColor;
            oAscii.rows[ 0 ].cells[ 0 ].style.color = oImg.style.color;

          }*/

          oAscii.cellSpacing = 0;
          oAscii.cellPadding = 0;

          const oStyle = oAscii.style;
          oStyle.display = 'inline';
          oStyle.width = Math.round(iWidth / fResolution * iScale) + 'px';
          oStyle.height = Math.round(iHeight / fResolution * iScale) + 'px';
          oStyle.whiteSpace = 'pre';
          oStyle.margin = '0px';
          oStyle.padding = '0px';
          oStyle.letterSpacing = fLetterSpacing + 'px';
          oStyle.fontFamily = strFont;
          oStyle.fontSize = fFontSize + 'px';
          oStyle.lineHeight = fLineHeight + 'px';
          oStyle.textAlign = 'left';
          oStyle.textDecoration = 'none';

      }


      const aDefaultCharList = (' .,:;i1tfLCG08@').split('');
      const aDefaultColorCharList = (' CGO08@').split('');
      const strFont = 'courier new, monospace';

      const oCanvasImg = renderer.domElement;

      const oCanvas = document.createElement('canvas',"2d");
      if (!oCanvas.getContext) {

          return;

      }
      const oCtx = oCanvas.getContext('2d');
      if (!oCtx.getImageData) {

          return;

      }

      let aCharList = (bColor ? aDefaultColorCharList : aDefaultCharList);

      if (charSet) aCharList = charSet;

      // Setup dom

      const fFontSize = (2 / fResolution) * iScale;
      const fLineHeight = (2 / fResolution) * iScale;

      // adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width.

      let fLetterSpacing = 0;

      if (strResolution == 'low') {

          switch (iScale) {

              case 1:
                  fLetterSpacing = -1;
                  break;
              case 2:
              case 3:
                  fLetterSpacing = -2.1;
                  break;
              case 4:
                  fLetterSpacing = -3.1;
                  break;
              case 5:
                  fLetterSpacing = -4.15;
                  break;

          }

      }

      if (strResolution == 'medium') {

          switch (iScale) {

              case 1:
                  fLetterSpacing = 0;
                  break;
              case 2:
                  fLetterSpacing = -1;
                  break;
              case 3:
                  fLetterSpacing = -1.04;
                  break;
              case 4:
              case 5:
                  fLetterSpacing = -2.1;
                  break;

          }

      }

      if (strResolution == 'high') {

          switch (iScale) {

              case 1:
              case 2:
                  fLetterSpacing = 0;
                  break;
              case 3:
              case 4:
              case 5:
                  fLetterSpacing = -1;
                  break;

          }

      }


      // can't get a span or div to flow like an img element, but a table works?
      async function asciifyImage(oAscii) {      
          //   console.error(oAscii)
          oCtx.clearRect(0, 0, iWidth, iHeight);
        
          oCtx.drawImage(await core.Canvas.toImage(oCanvas,oCanvasImg), 0, 0, iWidth, iHeight);
          const oImgData = oCtx.getImageData(0, 0, iWidth, iHeight).data;
 

          // Coloring loop starts now
          let strChars = '';

          // console.time('rendering');

          for (let y = 0; y < iHeight; y += 2) {

              for (let x = 0; x < iWidth; x++) {

                  const iOffset = (y * iWidth + x) * 4;

                  const iRed = oImgData[iOffset];
                  const iGreen = oImgData[iOffset + 1];
                  const iBlue = oImgData[iOffset + 2];
                  const iAlpha = oImgData[iOffset + 3];
                  let iCharIdx;

                  let fBrightness;

                  fBrightness = (0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue) / 255;
                  // fBrightness = (0.3*iRed + 0.5*iGreen + 0.3*iBlue) / 255;

                  if (iAlpha == 0) {

                      // should calculate alpha instead, but quick hack :)
                      //fBrightness *= (iAlpha / 255);
                      fBrightness = 1;

                  }

                  iCharIdx = Math.floor((1 - fBrightness) * (aCharList.length - 1));

                  if (bInvert) {

                      iCharIdx = aCharList.length - iCharIdx - 1;

                  }

                  // good for debugging
                  //fBrightness = Math.floor(fBrightness * 10);
                  //strThisChar = fBrightness;

                  let strThisChar = aCharList[iCharIdx];

                  if (strThisChar === undefined || strThisChar == ' ')
                      strThisChar = '&nbsp;';

                  if (bColor) {

                      strChars += '<span style=\'' +
                          'color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' +
                          (bBlock ? 'background-color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' : '') +
                          (bAlpha ? 'opacity:' + (iAlpha / 255) + ';' : '') +
                          '\'>' + strThisChar + '</span>';

                  } else {

                      strChars += strThisChar;

                  }

              }

              strChars += '<br/>';

          }
          oAscii.innerHTML =strChars ;

      
          // console.timeEnd('rendering');

          // return oAscii;

      }

  }

}

export {
  AsciiEffect
};